"""Scipy的ODR正交距离回归（ODR-Orthogonal Distance Regression）模块，
适用于回归分析时，因变量和自变量之间存在非线性关系的情况。
它提高了回归分析的准确性和稳健性。对于需要解决非线性回归问题的科研人员和工程师来说，它具有非常重要的意义。
ODR正交距离回归模块的作用主要在于它将正交化方法和距离回归结合起来，解决了传统线性回归模型在处理非线性问题时的局限性。
它通过将自变量进行正交化处理，使得因变量和自变量之间的非线性关系能够更好地被拟合出来。
1. 主要功能
scipy.odr模块针对的领域比较明确，所以不像之前介绍的模块有那么多函数。
此模块的主要函数包括：
函数名	说明
Data	要拟合的数据
RealData	数据的权重为实际标准差和/或协方差
Model	Model 类存储有关您希望拟合的函数的信息
ODR	ODR 类收集所有信息并协调主要拟合例程的运行
Output	输出类存储 ODR 运行的输出
其他函数	调整拟合和模型的一些函数
一般来说，使用前5个函数，就可以进行一些正交距离回归分析。

2. 使用示例
正交距离分析一般步骤如下：



2.1. 准备数据
数据采用以前收集的江苏省人口数据，获取地址：https://databook.top/jiangsustat/renkou
"""
import pandas as pd
from matplotlib.ticker import MultipleLocator
import matplotlib.pyplot as plt
import scipy.odr as sodr
import matplot_config

matplot_config.init_config()

data = pd.read_csv("./datas/人口/人口-年末常住人口（万人）.csv")
data.head(10)
"""image.png
一共31条数据，1990年~2020年江苏省的人口变化数据。

用散点图看看数据的变化趋势："""


ax = plt.subplot()
ax.scatter(data["year"], data["value"], marker='*', color='r')
ax.xaxis.set_major_locator(MultipleLocator(5))
ax.set_title("江苏省人口变化")

plt.show()
"""image.png

2.2. 创建模型
使用scipy.odr模块中的Model函数创建一个拟合的模型。
"""
# 模型函数
def model_func(p, x):
    k, b = p
    return k * x + b

model = sodr.Model(model_func)
# 2.3. 生成数据
# 将上面的人口数据data转换为可以用于ODR运算的数据。

# x是数据
x = range(len(data))

# 转换数据用RealData或者Data函数都可以
rdata = sodr.RealData(x, data["value"])
# rdata = sodr.Data(x, data["value"])
"""Data和RealData函数都是用来构造数据的。
一般来说，Data函数用来构造理论数据；
RealData函数用来构造实际数据的，且RealData中还可以设置权重。

这里没有设置权重，用哪个函数都可以。

2.4. ODR运算
有了数据和模型之后，就可以进行ODR运算了。"""

odr = sodr.ODR(rdata, model, beta0=[0, 1])

result = odr.run()
result.pprint()
"""# 运行结果：
Beta: [  61.01340781 6724.77566283]
Beta Std Error: [ 1.11208495 19.3974215 ]
Beta Covariance: [[  1.51592414 -22.73886321]
 [-22.73886321 461.20026764]]
Residual Variance: 0.8158277156001223
Inverse Condition #: 0.2520617152422754
Reason(s) for Halting:
  Sum of squares convergence
其中 Beta 和 Beta Std Error就拟合的参数值和参数的标准差。

2.5. 输出结果
根据计算结果，绘制出图形更容易理解。
"""
# 拟合参数和参数的标准差
beta = result.beta
beta_std = result.sd_beta

# 拟合的曲线
y = beta[0] * x + beta[1]

# 拟合曲线的标准差上限
y_up = (beta[0] + beta_std[0]) * x + (beta[1]+ beta_std[1])

# 拟合曲线的标准差下限
y_down = (beta[0] - beta_std[0]) * x + (beta[1] - beta_std[1])

# 绘制拟合的曲线
ax = plt.subplot()
ax.scatter(data["year"], data["value"], marker='*', color='r')
ax.xaxis.set_major_locator(MultipleLocator(5))
ax.set_title("江苏省人口变化")

ax.plot(x, y, color="b", label="拟合曲线")
ax.plot(x, y_up, color="y", label="标准差上限")
ax.plot(x, y_down, color="g", label="标准差下限")

plt.legend()
plt.show()
"""image.png

这就是通过ODR模块拟合的人口变化情况。

3. 总结
ODR正交距离回归之所以作为Scipy的单独模块，是因为它是一种特殊的曲线拟合方法，
它使用正交化和距离加权的最小二乘法来处理具有非线性关系的输入变量，并旨在找到最优的模型以最小化预测误差。
这与一般的曲线拟合在方法和目标上有很大的不同。

后续介绍Scipy库中的其他模块时，还会介绍其他的曲线拟合函数，到时候可以和这里的ODR方法对照比较一下。"""